#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <iostream>
#include <sqlite3.h>

#include <vector>
#include <string>
using namespace std;

#include "md5er.h"
#include "FileDB.h"

void crawl(FileDB& db, string path, int pathId) {
  cout << "crawl\t" << path << endl;
  DIR* d = opendir(path.c_str());

  struct dirent* e;
  struct stat sb;

  while ((e = readdir(d)) != NULL) {
    // construct full path
    string ePath = path + '/' + e->d_name;
    
    // what kind of a file is this? avoid symlinks because of the recursion problem.
    lstat(ePath.c_str(), &sb);

    // branch on type of file
    if (S_ISDIR(sb.st_mode)) {
      // directory

      // skip '.' and '..'
      if (strcmp(e->d_name, ".") == 0) continue;
      if (strcmp(e->d_name, "..") == 0) continue;

      cout << "dir\t" << ePath << endl;

      // talk with the database about this child directory.
      int childId = db.get_dir_id(e->d_name, pathId);
      if (childId != 0) {
        // child is in the database. crawl it.
        crawl(db, ePath, childId);
      } else {
        // child is not in the database. add it.
        db.add_dir(e->d_name, pathId);

        // and continue crawling.
        childId = db.get_dir_id(e->d_name, pathId);
        crawl(db, ePath, childId);
      }
    } else if (S_ISREG(sb.st_mode)) {
      // regular file -- more interesting!
      cout << "file\t" << ePath << endl;

      // fetch file from DB
      int contentsId;
      time_t mTime;
      int fileId = db.get_file_id(pathId, e->d_name, &contentsId, &mTime);

      if (fileId != 0) {
        // file is in database. Check modification times
        if (mTime < sb.st_mtime) {
          // file on disk is newer.
          cout << "newer\t" << ePath << endl;

          char md5[33];
          md5_file(ePath.c_str(), md5);
          cout << "-md5\t" << md5 << endl;

          // update database
          db.update_contents(fileId, contentsId, md5, sb.st_mtime);
        } else if (mTime == sb.st_mtime) {
          cout << "indb\t" << ePath << endl;
        } else { // file in database is /newer/... bad.
          cout << "badfile\t" << ePath << endl;
        }
      } else {
        // file is not in the database. add it.
        char md5[33];
        cout << "md5\t" << ePath;
        md5_file(ePath.c_str(), md5);
        cout << endl;
        
        db.add_file(pathId, e->d_name, sb.st_size, md5, sb.st_mtime);
      }
    } else {
      // some other type of file... ?
      cout << "other\t" << ePath << endl;
    }
  }

  closedir(d);
}

int main (int argc, char* argv[]) {
  if (argc != 3) {
    cerr << "Usage: " << argv[0] << " <db> <directory> " << endl;
    return 1;
  }

  FileDB db(argv[1]);
  crawl(db,argv[2], 0);
  return 0;
}
